package baseFx.database.execute.impl;

import baseFx.common.Assert;
import baseFx.common.Stream;
import baseFx.common.utils.StringUtils;
import baseFx.database.DbEntity;
import baseFx.database.ProcName;
import baseFx.database.Sql;
import baseFx.database.SqlParam;
import baseFx.database.execute.AbstractDbExecute;
import baseFx.database.execute.SqlInsertContext;
import baseFx.database.execute.SqlUpdateContext;
import baseFx.database.query.impl.MysqlQuery;
import baseFx.database.where.ExecuteWhere;
import baseFx.database.where.impl.MysqlWhere;

import java.util.Map;

public class MysqlExecute extends AbstractDbExecute {
    private Sql sql;

    public MysqlExecute(Sql sql) {
        super(sql, new MysqlQuery(sql));
        this.sql = sql;
    }

    private String getProcName(Object model) {
        ProcName annotation = model.getClass().getAnnotation(ProcName.class);
        return annotation == null ? model.getClass().getSimpleName() : annotation.value();
    }

    @Override
    protected SqlParam createInsert(SqlInsertContext context) {
        DbEntity entity = DbEntity.get(context.table);
        Map<String, Object> data = entity.getValues(context.model);
        String columns = "(" + String.join(",", data.keySet().stream().map(i -> escapeField(i)).toArray(String[]::new)) + ")";
        String values = "(" + String.join(",", data.keySet().stream().map(i -> paramName(i)).toArray(String[]::new)) + ")";
        SqlParam param = new SqlParam();
        param.command = String.join(" ", "insert into", escapeField(getTableName(context.table)) + columns, "values" + values);
        param.args.putAll(data);
        param.timeout = context.timeout;
        return param;
    }

    @Override
    protected String getIncSql(String column) {
        return "SELECT LAST_INSERT_ID()";
    }

    @Override
    protected SqlParam createUpdate(SqlUpdateContext context) {
        DbEntity entity = DbEntity.get(context.setTable);
        Map<String, Object> setPs = entity.getValues(context.set);
        Assert.isNotEquals(setPs.size(), 0, "Update没有任何更新");
        SqlParam param = new MysqlWhere(sql).parse(context.where);
        String sets;
        if (context.isSetNull) {
            sets = Stream.create(setPs.keySet()).map(i -> escapeField(i) + "=NULL").join(",");
        } else {
            sets = Stream.create(setPs.keySet()).map(i -> {
                String pn = "set_" + i;
                param.args.put(pn, setPs.get(i));
                return escapeField(i) + "=" + paramName(pn);
            }).join(",");
        }
        String sql = "update " + escapeField(getTableName(context.setTable));
        if (StringUtils.hasValue(context.where.index())) {
            sql += " force index(`" + context.where.index() + "`)";
        }
        param.command = sql + " set " + sets + param.command;
        param.timeout = context.timeout;
        return param;
    }

    @Override
    protected SqlParam createDelete(ExecuteWhere<?> where, Integer timeout) {
        DbEntity entity = DbEntity.get(where.getTable());
        SqlParam param = new MysqlWhere(sql).parse(where);
        String sql = "delete from " + escapeField(getTableName(where.getTable()));
        if (StringUtils.hasValue(where.index())) {
            sql += " force index(`" + where.index() + "`)";
        }
        param.command = sql + param.command;
        param.timeout = timeout;
        return param;
    }

    @Override
    protected SqlParam createProc(Object model, Integer timeout) {
        DbEntity entity = DbEntity.get(model.getClass());
        SqlParam param = new SqlParam();
        param.command = getProcName(model);
        param.args.putAll(entity.getValues(model));
        param.timeout = timeout;
        param.isProc = true;
        return param;
    }
}
